/**
 * @author mixed
 */
describe('JSMock에서', {
	'before': function(){
		document.getElementById("mock_test1").value = "";
		document.getElementById("mock_test2").value = "";
		document.getElementById("mock_test3").value = "";
	},
 	   '첫 번째 인자는 string,object여야 한다.': function(){
  		mock("FOO");
  	        value_of(FOO).should_be({});
  		
  		var BAR = {};
  		mock(BAR);
  	        value_of(BAR).should_be(BAR);
  	    },
  	    '두 번째 인자는 없거나 "instance","object" 문자여야 한다.': function(){
  		mock("FOO2");
  		value_of(FOO2).should_be({});
  		
  		mock("FOO3",Mock.INSTANCE);
  		value_of(FOO3.prototype).should_be({});
  	    },
  	'should_receive는 mock객체에 메소드를 셋팅한다.':function(){
  		mock("RECEIVE").should_receive("test");
  		value_of(typeof RECEIVE.test == "function").should_be_true();
  		
  		mock("RECEIVE2",Mock.INSTANCE).should_receive("test");
  		value_of(typeof RECEIVE2.prototype.test == "function").should_be_true();
  	},
  	'should_receive의 반환 값은 추가 오브 젝트 이다.':function(){
  		var mock_method = mock("RECEIVE3").should_receive("test");
  		value_of(mock_method instanceof MockMethod).should_be_true();
  	},
  	'and_return은 정상적으로 해당 셋팅된 값을 리턴해야 한다.':function(){
  		mock("RECEIVE4").should_receive("test").and_return(3);
  		value_of(RECEIVE4.test()).should_be(3);
  	},
  	'with_param 을 설정한 경우에는 with_param에 맞는 경우만 and_return에 있는 값을 리턴해야 한다.':function(){
  		mock("RECEIVE4").should_receive("test").with_param(1,2).and_return(4);
  		value_of(RECEIVE4.test(1,2)).should_be(4);
  	},
  	'같은 함수에 파라메터만 변경되었을때에도 기존의 기능은 정상 작동해야 한다.':function(){
  		mock("RECEIVE4").should_receive("test").with_param(1,2,3).and_return(5);
  		
  		value_of(RECEIVE4.test()).should_be(3);
  		value_of(RECEIVE4.test(1,2)).should_be(4);
  		value_of(RECEIVE4.test(1,2,3)).should_be(5);
  	},
  	'같은 함수, 같은 파라메터이고 return만 변경되었을때에 새로 적용된 return값이 리턴 되어야 한다.':function(){
  		value_of(RECEIVE4.test(1,2,3)).should_be(5);
  		
  		mock("RECEIVE4").should_receive("test").with_param(1,2,3).and_return(6);
  		
  		value_of(RECEIVE4.test()).should_be(3);
  		value_of(RECEIVE4.test(1,2)).should_be(4);
  		value_of(RECEIVE4.test(1,2,3)).should_be(6);
  	},
  	'and_throw은 정상적으로 해당 셋팅된 예외을 던져야 한다.':function(){
  		mock("RECEIVE4").should_receive("test2").and_throw(new Error("and_throw test."));
  		var error_message = "";
  		try{
  			RECEIVE4.test2();
  		}catch(e){
  			error_message = e.message;
  		}
  		value_of(error_message).should_be("and_throw test.");
  		
  	},
  	'with_param 을 설정한 경우에는 with_param에 맞는 경우만 해당 셋팅된 예외을 던져야 한다.':function(){
  		mock("RECEIVE4").should_receive("test2").with_param(1,2).and_throw(new Error("and_throw test2."));
  		
  		var error_message = "";
  		try{
  			RECEIVE4.test2(1,2);
  		}catch(e){
  			error_message = e.message;
  		}
  		
  		value_of(error_message).should_be("and_throw test2.");
  	}
  	,
  	'같은 함수에 파라메터만 변경되었을때에도 기존의 기능(and_throw)은 정상 작동해야 한다.':function(){
  		
  		mock("RECEIVE4").should_receive("test2").with_param(1,2,3).and_throw(new Error("and_throw test3."));
  		
  		var error_message = "";
  		try{
  			RECEIVE4.test2();
  		}catch(e){
  			error_message = e.message;
  		}
  		value_of(error_message).should_be("and_throw test.");
  		
  		try{
  			RECEIVE4.test2(1,2);
  		}catch(e){
  			error_message = e.message;
  		}
  		value_of(error_message).should_be("and_throw test2.");
  		
  		try{
  			RECEIVE4.test2(1,2,3);
  		}catch(e){
  			error_message = e.message;
  		}
  		value_of(error_message).should_be("and_throw test3.");
  		
  	},
  	'같은 함수, 같은 파라메터이고 thorw만 변경되었을때에 새로 셋팅된 예외을 던져야 한다.':function(){
  		var error_message = "";
  		
  		try{
  			RECEIVE4.test2(1,2,3);
  		}catch(e){
  			error_message = e.message;
  		}
  		
  		value_of(error_message).should_be("and_throw test3.");
  		
  		mock("RECEIVE4").should_receive("test2").with_param(1,2,3).and_throw(new Error("and_throw test4."));
  		
  		try{
  			RECEIVE4.test2(1,2,3);
  		}catch(e){
  			error_message = e.message;
  		}
  		value_of(error_message).should_be("and_throw test4.");
  	},
  	'and_function은 정상적으로 해당 셋팅된 함수를 실행 시켜야 한다.':function(){
  		
  		value_of(document.getElementById("mock_test1").value).should_be("");
  		mock("RECEIVE4").should_receive("test3").and_function(function(){
  			document.getElementById("mock_test1").value="first";
  		});
  		RECEIVE4.test3();
  		value_of(document.getElementById("mock_test1").value).should_be("first");
  		
  	},
  	'with_param 을 설정한 경우에는 with_param에 맞는 경우만 해당 함수가 실행된다.':function(){
  		value_of(document.getElementById("mock_test2").value).should_be("");
  		
  		mock("RECEIVE4").should_receive("test3").with_param(1,2).and_function(function(){
  			document.getElementById("mock_test2").value="second";
  		});
  		RECEIVE4.test3(1,2);
  		
  		value_of(document.getElementById("mock_test2").value).should_be("second");
  	},
  	'같은 함수에 파라메터만 변경되었을때에도 기존의 기능은 정상 작동해야 한다3.':function(){
  		
  		value_of(document.getElementById("mock_test3").value).should_be("");
  		
  		mock("RECEIVE4").should_receive("test3").with_param(1,2,3).and_function(function(){
  			document.getElementById("mock_test3").value="third";
  		});
  		RECEIVE4.test3(1,2,3);
  		
  		value_of(document.getElementById("mock_test3").value).should_be("third");
  		
  		value_of(document.getElementById("mock_test1").value).should_be("");
  		RECEIVE4.test3();
  		value_of(document.getElementById("mock_test1").value).should_be("first");
  		
  		value_of(document.getElementById("mock_test2").value).should_be("");
  		RECEIVE4.test3(1,2);
  		value_of(document.getElementById("mock_test2").value).should_be("second");
  	},
  	'같은 함수, 같은 파라메터이고 and_function만 변경되었을때에 새로 적용된 함수가 실행되어야 한다.':function(){
  		
  		RECEIVE4.test3(1,2,3);
  		value_of(document.getElementById("mock_test3").value).should_be("third");
  		
  		mock("RECEIVE4").should_receive("test3").with_param(1,2,3).and_function(function(){
  			document.getElementById("mock_test3").value="third2";
  		});
  		
  		RECEIVE4.test3(1,2,3);
  		value_of(document.getElementById("mock_test3").value).should_be("third2");
  		
  	},
  	'instance 타입도 정상 작동 되어야 한다.':function(){
  		mock("MockInstance",Mock.INSTANCE).should_receive("test").with_param(1).and_return("1");
  		mock("MockInstance",Mock.INSTANCE).should_receive("test").with_param(2).and_throw(new Error("test"));
  		mock("MockInstance",Mock.INSTANCE).should_receive("test").with_param(3).and_function(function(){
  			document.getElementById("mock_test1").value="test";
  		});
  		
  		var mockInstance = new MockInstance();
  		
  		value_of(mockInstance.test(1)).should_be("1");
  		
  		var errormessage = ""; 
  		try{
  			mockInstance.test(2);
  		}catch(e){
  			errormessage = e.message;
  		}
  		value_of(errormessage).should_be("test");
  		
  		value_of(document.getElementById("mock_test1").value).should_be("");
  		mockInstance.test(3);
  		value_of(document.getElementById("mock_test1").value).should_be("test");
  		
  		
  		
  	},
  	'일반 함수도 정상 작동 해야 한다.':function(){
  		mock(window).should_receive("commonfunc").with_param(1).and_return("1");
  		mock(window).should_receive("commonfunc").with_param(2).and_throw(new Error("test2"));
  		mock(window).should_receive("commonfunc").with_param(3).and_function(function(){
  			document.getElementById("mock_test1").value="commonfunc";
  		});
  		
  		
  		value_of(commonfunc(1)).should_be("1");
  		
  		var errormessage = ""; 
  		try{
  			commonfunc(2);
  		}catch(e){
  			errormessage = e.message;
  		}
  		value_of(errormessage).should_be("test2");
  		
  		value_of(document.getElementById("mock_test1").value).should_be("");
  		commonfunc(3);
  		value_of(document.getElementById("mock_test1").value).should_be("commonfunc");
  	},
  	'param에 anything이 들어가면 해당 파라메터에는 어떤 것이 와도 상관 없이 호출되어야 한다.':function(){
  		mock("Foo").should_receive("test").with_param(1,2).and_return("1");
  		mock("Foo").should_receive("test").with_param(1,2,Mock.anything()).and_return("2");
  		mock("Foo").should_receive("test").with_param(1,2,Mock.anything(),Mock.anything()).and_return("3");
  		
  		value_of(Foo.test(1,2)).should_be("1");
  		value_of(Foo.test(1,2,3)).should_be("2");
  		value_of(Foo.test(1,2,1)).should_be("2");
  		value_of(Foo.test(1,2,4)).should_be("2");
  		value_of(Foo.test(1,2,4,5)).should_be("3");
  		value_of(Foo.test(1,2,4,6)).should_be("3");
  	},
  	'param에 anything이 들어간 파라메터에서 다시 설정한 경우에 다시 설정한 값으로 반환 해야 한다.':function(){
  		mock("Foo").should_receive("test").with_param("5","6",Mock.anything()).and_return("5");
  		value_of(Foo.test("5","6","3")).should_be("5");
  		
  		mock("Foo").should_receive("test").with_param("5","6",Mock.anything()).and_return("6");
  		value_of(Foo.test("5","6","3")).should_be("6");
  		
  	},
  	'namespace를 사용한 경우에도 정상적으로 사용 가능해야 한다.':function(){
  		mock("Test").should_receive("test").and_return("test");
  		value_of(Test.test()).should_be("test");
  		
  		mock("aaa.bbb.ccc.ddd").should_receive("test").and_return("test");
  		value_of(aaa.bbb.ccc.ddd.test()).should_be("test");
  		
  		mock("aaaa.bbbb.cccc",Mock.INSTANCE).should_receive("test").and_return("test");
  		value_of(new aaaa.bbbb.cccc().test()).should_be("test");
  		
  		window["aaaaa"] = {};
  		aaaaa.bbbbb = {};
  		
  		mock("aaaaa.bbbbb").should_receive("test").and_return("test");
  		value_of(aaaaa.bbbbb.test()).should_be("test");
  		
  		mock("aaaaa.bbbbb.ccccc").should_receive("test").and_return("test");
  		value_of(aaaaa.bbbbb.ccccc.test()).should_be("test");
  	},
  	'namespace를 사용한 경우에도 param도 정상 작동 되어야 한다.':function(){
  		
  		mock("param.test").should_receive("commonfunc").with_param(1).and_return("1");
  		mock("param.test").should_receive("commonfunc").with_param(2).and_throw(new Error("test2"));
  		mock("param.test").should_receive("commonfunc").with_param(3).and_function(function(){
  			document.getElementById("mock_test1").value="commonfunc";
  		});
  		
  		
  		value_of(param.test.commonfunc(1)).should_be("1");
  		
  		var errormessage = ""; 
  		try{
  			param.test.commonfunc(2);
  		}catch(e){
  			errormessage = e.message;
  		}
  		value_of(errormessage).should_be("test2");
  		
  		value_of(document.getElementById("mock_test1").value).should_be("");
  		param.test.commonfunc(3);
  		value_of(document.getElementById("mock_test1").value).should_be("commonfunc");
  	},
	"verify는 정상적으로 호춯되어야 한다.": function(){
		mock("Verify").should_receive("kall").with_param(1,2).and_return("1");
		mock("Verify").should_receive("kall2").and_return("1");
		
		Verify.kall(1,2);
		var compareObj  = {"total":1, "param":{}};
		compareObj.param[_toSource([1, 2])] = 1;
		value_of(mock("Verify").verify("kall")).should_be(compareObj);
		try{
			mock("Verify").verify("kall2");
		}catch(e){
			value_of(e.message).should_be("kall2 is not called.");
		}
		
		try{
			mock("Verify").verify("kall3");
		}catch(e){
			value_of(e.message).should_be("kall3 isn't method.");
		}
		
	},
	"verify_all은 전체를 확인이 해야 한다.":function(){
		mock("Verify2").should_receive("kall").with_param(1,2).and_return("1");
		mock("Verify2").should_receive("kall2").and_return("1");
		Verify2.kall(1,2);
		Verify2.kall2();
		var compareObj  = {"kall":{"total":1, "param":{}}, "kall2":{"total":1, "param":{}}};
		compareObj.kall.param[_toSource([1, 2])] = 1;
		compareObj.kall2.param[_toSource([])] = 1;
		
		value_of(mock("Verify2").verify_all()).should_be(compareObj);
		
		mock("Verify3").should_receive("kall").with_param(1,2).and_return("1");
		try{
			mock("Verify3").verify_all();
		}catch(e){
			value_of(e.message).should_be("kall is not called.");
		}
	},
	"clear은 정상적으로 값이 삭제되어야 한다.":function(){
		mock("Verify4").should_receive("kall").with_param(1,2).and_return("1");
		Verify4.kall(1,2);
		var compareObj  = {"kall":{"total":1, "param":{}}};
		compareObj.kall.param[_toSource([1, 2])] = 1;
		value_of(mock("Verify4").verify_all()).should_be(compareObj);
		mock("Verify4").clear("kall");
		try{
			mock("Verify4").verify("kall");
		}catch(e){
			value_of(e.message).should_be("kall is not called.");
		}
	},
	"clear_all은 전체를 확인이 해야 한다.":function(){
		mock("Verify5").should_receive("kall").with_param(1,2).and_return("1");
		mock("Verify5").should_receive("kall2").and_return("1");
		Verify5.kall(1,2);
		Verify5.kall2();
		var compareObj  = {"kall":{"total":1, "param":{}}, "kall2":{"total":1, "param":{}}};
		compareObj.kall.param[_toSource([1, 2])] = 1;
		compareObj.kall2.param[_toSource([])] = 1;
		value_of(mock("Verify5").verify_all()).should_be(compareObj);
		mock("Verify5").clear_all();
		try{
			mock("Verify5").verify_all();
		}catch(e){
			value_of(e.message).should_be("kall is not called.");
		}
	},
	"기존에 있는 클래스에 메소드를 추가할 수 있는가?":function(){
		function KlassTest(){
		}
		KlassTest.prototype.test = function(){
			return 1;
		}
		mock(KlassTest,Mock.INSTANCE).should_receive("kall").and_return(2);
		var klass = new KlassTest();
		value_of(klass.kall()).should_be(2);
	},
	"기존에 메서드가 있는 경우 되돌릴 수 있어야 한다.":function(){
		//Given
		function KlassTest(){
		}
		KlassTest.prototype.test = function(){
			return 1;
		}
		mock(KlassTest,Mock.INSTANCE).should_receive("test").and_return(2);
		//when
		var klass = new KlassTest();
		//then
		value_of(klass.test()).should_be(2);
		
		//given
		mock(KlassTest,Mock.INSTANCE).restore("test");
		//when
		var klassReset = new KlassTest();
		//then
		value_of(klassReset.test()).should_be(1);
		
		mock(KlassTest,Mock.INSTANCE).should_receive("test").and_return(2);
		//when
		var klass2 = new KlassTest();
		//then
		value_of(klass2.test()).should_be(2);
	},
	"기존에 메서드가 여러개 있는 경우 한번에 되돌릴 수 있어야 한다.":function(){
		//Given
		function KlassTest(){
		}
		KlassTest.prototype.test = function(){
			return 1;
		}
		KlassTest.prototype.test2 = function(){
			return 2;
		}
		mock(KlassTest,Mock.INSTANCE).should_receive("test").and_return(3);
		mock(KlassTest,Mock.INSTANCE).should_receive("test2").and_return(4);
		//when
		var klass = new KlassTest();
		//then
		value_of(klass.test()).should_be(3);
		value_of(klass.test2()).should_be(4);
		
		//given
		mock(KlassTest,Mock.INSTANCE).restore_all();
		//when
		var klassReset = new KlassTest();
		//then
		value_of(klassReset.test()).should_be(1);
		value_of(klassReset.test2()).should_be(2);
	},
	"기존에 메서드가 없을 경우 reset을 호출할 때 에러나 발생하면 안된다.":function(){
		//Given
		function KlassTest(){
		}
		mock(KlassTest,Mock.INSTANCE).should_receive("test").and_return(2);
		//when
		var klass = new KlassTest();
		//then
		value_of(klass.test()).should_be(2);
		try{
			mock(KlassTest,Mock.INSTANCE).restore("test");
		}catch(e){
			value_of().should_be_false();
		}
	}
});

